from MagicAdmin.models import Application
from MagicAdmin.models import Model
from MagicAdmin.models import Field
from MagicAdmin.models import Option
from MagicAdmin.models import OptionType
from MagicAdmin.models import FieldType
from MagicAdmin.models import FieldOption

from django.contrib import admin
from django.forms import ModelChoiceField
from django.forms import ModelForm
from django.forms import Select
from django.forms import ValidationError
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
from django.http import HttpResponse
from django.forms.models import BaseInlineFormSet
from django import dispatch

get_required = dispatch.Signal(providing_args=[])

def flatatt(attrs):
    return u''.join([u' %s="%s"' % (k, conditional_escape(v)) for k, v in attrs.items()])

class OptionTypeWidget(Select):
    def __init__(self, attrs=None, choices=()):
        super(OptionTypeWidget, self).__init__(attrs)
        self.choices = list(choices)
        self.type_map={}

    def render_option(self, selected_choices, option_value, option_label):
        label = option_label
        if "**" in option_label:
            label, type = option_label.split("**")
            self.type_map[option_value] = type

        return super(OptionTypeWidget, self).render_option(selected_choices, option_value, label)

    def return_input_call(self, data_type, value_box_id, value_box_name, value="''"):       
        if data_type == "boolean":
            input = "addCheckbox('%s', '%s', %s);" % (value_box_id, value_box_name, value)
        elif data_type == "big_text":
            input = "addTextArea('%s', '%s', %s);" % (value_box_id, value_box_name, value)
        elif data_type == "model":
            input = "addSelectModels('%s', '%s', %s);" % (value_box_id, value_box_name, value)
        else:
            input = "addInput('%s', '%s', %s);" % (value_box_id, value_box_name, value)
        return input

    
    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = ''
        options = self.render_options(choices, [value])

        value_box_id = attrs["id"].replace("option_type", "value")
        value_box_name = value_box_id.replace("id_", "")

        javascript = "OnChange=\"javascript: "
        for k, v in self.type_map.items():
            javascript += "if (this.options[this.selectedIndex].value == %s){ removeInput('%s'); %s}; " % (k, value_box_id, self.return_input_call(v, value_box_id, value_box_name))

        if value:
            refresh_function = u'''
                 <script>
                     function %s_refresh(element) {
                         value = document.getElementById(element).value;
                         removeInput('%s');
                         %s
                     }
                 </script>
            ''' % (value_box_id.replace("-",""), value_box_id, self.return_input_call(self.type_map[int(value)], value_box_id, value_box_name, "value"))
        
        javascript += "\""

        final_attrs = self.build_attrs(attrs, name=name)
        output = []
        if value: output.append(refresh_function.strip())
        output .append(u'<select %s %s>' % (javascript, flatatt(final_attrs)))
        if options:
            output.append(options)
        output.append(u'</select>')
        return mark_safe(u'\n'.join(output))

class OptionTypeModelChoiceField(ModelChoiceField):
    widget = OptionTypeWidget

class FieldOptionForm(ModelForm):
    option_type = OptionTypeModelChoiceField(OptionType.objects.all())

    class Meta:
        model = OptionType

    def clean_value(self):
        data = self.cleaned_data["value"]
        try:
            if self.cleaned_data["option_type"].is_boolean and data != "True":
                data = "False"
        except:
            raise ValidationError("Option type can not be empty!!")
        if self.cleaned_data["option_type"].is_numeric and not data.isdigit():
            raise ValidationError("Value must be a number!")
        if self.cleaned_data["option_type"].is_model and not data:
            raise ValidationError("You must select a model!")
        if not self.cleaned_data["option_type"] in self.cleaned_data["field"].type.option_types.all():
            raise ValidationError("Option Type not allowed for this Field Type")
        return data

r = []
def getting_required_fields(sender, **kwargs):
    try:
        required_fields = sender.cleaned_data["type"].required_option_types.all()
    except:
        return
    while r:
        r.pop()
    for req in required_fields:
        r.append(req)

get_required.connect(getting_required_fields)

class FieldOptionBaseInlineFormSet(BaseInlineFormSet):    
    def clean(self):       
        options = []
        deleted_options = []
        try:
            field = self.cleaned_data[0]["field"]
        except:
            for required_field in r:
                raise ValidationError("Required Option missing: %s" % (required_field.name))
            return
        for form in self.forms:
            if form.cleaned_data:
                if form.cleaned_data["DELETE"]:
                    deleted_options.append(form.cleaned_data["option_type"])
                if form.cleaned_data["option_type"] in options:
                    raise ValidationError("Can not add 2 options of the same type!!")
                options.append(form.cleaned_data["option_type"])
        for type in field.type.required_option_types.all():
            if type not in options:
                raise ValidationError("Required Option missing: %s" % (type.name))
            if type in deleted_options:
                raise ValidationError("Can not delete a Required Option!!")

class FieldAdminForm(ModelForm):
    class Meta:
        model = Field

    def clean(self):
        try:
            name = self.cleaned_data["name"]
        except:
            raise ValidationError("Field name can not be empty!")
        az = "abcdefghijklmnopqrstuvwxyz"
        az += az.upper()
        for l in name:
            if l not in az:
                raise ValidationError("Not a valid Field Name!!! ('%s' is not valid)" % l)
        get_required.send(sender=self)
        return self.cleaned_data

class OptionsInline(admin.TabularInline):
    template='MagicAdmin/AdminInLine/tabular_options.html'
    model = Option
    extra = 3
    form = FieldOptionForm
    formset = FieldOptionBaseInlineFormSet


class FieldOptionsInLine(admin.TabularInline):
    template='MagicAdmin/AdminInLine/tabular.html'
    model = FieldOption
    extra = 1

class FieldAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {
            'fields': ('name', 'human_readable_name', 'type')
            }),
        ]
    inlines = [OptionsInline]
    form = FieldAdminForm

    def response_change(self, request, obj, *args, **kwargs):
        if request.REQUEST.has_key('_popup'):  
            return HttpResponse('''
               <script type="text/javascript">
                   opener.dismissAddAnotherPopup(window, '', '');
               </script>''')  
        else:  
            return super(FieldAdmin, self).response_change(request, obj, *args, **kwargs)
    
class ModelAdminForm(ModelForm):
    class Meta:
        model = Model

    def clean(self):
        try:
            name = self.cleaned_data["name"]
        except:
            raise ValidationError("Model name can not be empty!")
        az = "abcdefghijklmnopqrstuvwxyz"
        az += az.upper()
        for l in name:
            if l not in az:
                raise ValidationError("Not a valid Model Name!!! ('%s' is not valid)" % l)
        return self.cleaned_data


class ModelAdmin(admin.ModelAdmin):
    inlines = [FieldOptionsInLine]
    form = ModelAdminForm
    def response_change(self, request, obj, *args, **kwargs):
        myPostData = request.POST.copy()
        if myPostData["must_save_and_continue"] == "True":
            myPostData["_continue"] = "Save and continue editing"
            request.POST.update(myPostData)
        return super(ModelAdmin, self).response_change(request, obj, *args, **kwargs)

    def response_add(self, request, obj, *args, **kwargs):
        myPostData = request.POST.copy()
        if myPostData["must_save_and_continue"] == "True":
            myPostData["_continue"] = "Save and continue editing"
            request.POST.update(myPostData)
        return super(ModelAdmin, self).response_add(request, obj, *args, **kwargs)

admin.site.register(Application)
admin.site.register(Model, ModelAdmin)
admin.site.register(Field, FieldAdmin)
admin.site.register(Option)
#admin.site.register(OptionType)
#admin.site.register(FieldType)
